package com.atguigu.srb.core.service.impl;

import com.alibaba.fastjson.JSON;
import com.atguigu.common.exception.BusinessException;
import com.atguigu.common.results.ResultCodeEnum;
import com.atguigu.srb.base.utils.JwtUtils;
import com.atguigu.srb.core.hfb.FormHelper;
import com.atguigu.srb.core.hfb.HfbConst;
import com.atguigu.srb.core.hfb.RequestHelper;
import com.atguigu.srb.core.pojo.entity.TransFlow;
import com.atguigu.srb.core.pojo.entity.UserAccount;
import com.atguigu.srb.core.mapper.UserAccountMapper;
import com.atguigu.srb.core.pojo.entity.UserInfo;
import com.atguigu.srb.core.service.TransFlowService;
import com.atguigu.srb.core.service.UserAccountService;
import com.atguigu.srb.core.service.UserInfoService;
import com.atguigu.srb.core.util.*;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.RequestHeader;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * <p>
 * 用户账户 服务实现类
 * </p>
 *
 * @author Atguigu
 * @since 2022-10-24
 */
@Service
@Slf4j
public class UserAccountServiceImpl extends ServiceImpl<UserAccountMapper, UserAccount> implements UserAccountService {
    @Autowired
    UserInfoService userInfoService;
    @Autowired
    TransFlowService transFlowService;//交易日志的业务类
    @Autowired
    RabbitTemplate rabbitTemplate;
    @Override
    public String chargeAmount(BigDecimal amount, String token) {
        //1、验证用户是否登录+账户是否正常+是否绑定过汇付宝账户
        UserInfo userInfo = userInfoService.checkAccount(token);
        //2、判断用户充值的金额不能小于等于0
        if(amount.compareTo(new BigDecimal("0"))!=1){
            throw new BusinessException(ResultCodeEnum.CHARGE_AMOUNT_LESS_ERROR);
        }

        //3、生成访问汇付宝充值页面需要的携带请求参数和汇付宝充值页面接口的自动提交的表单字符串
        //参数1：url代表构建的表单自动提交的地址  (汇付宝的充值接口地址)
        //参数2：用户本次充值时 汇付宝平台需要的参数(参考汇付宝的接口文档)
        Map<String, Object> params = new HashMap<>();
        params.put("agentId",HfbConst.AGENT_ID);//给商户分配的唯一标识  当前srb项目在hfb中的唯一的id
        //一般会保存到本地数据库：
        String chargeNo = LendNoUtils.getChargeNo();
        params.put("agentBillNo",chargeNo);//商户充值单号（要求唯一）  本次充值的srb生成的订单编号
        params.put("bindCode",userInfo.getBindCode());//充值人绑定协议号  用户绑定hbf时的唯一的编号
        params.put("chargeAmt",amount);//充值金额，即充值到汇付宝的金额。支持小数点后2位
        params.put("feeAmt",new BigDecimal("0"));//商户收取用户的手续费。支持小数点后2位。可以传0
        params.put("notifyUrl",HfbConst.RECHARGE_NOTIFY_URL);//充值完成 hfb回调的接口路径
        params.put("returnUrl",HfbConst.RECHARGE_RETURN_URL);//充值完成 用户在hfb平台点击返回时同步访问的页面地址
        params.put("timestamp",System.currentTimeMillis());//时间戳
        params.put("sign", RequestHelper.getSign(params));//签名：保证数据传递安全
        String form = FormHelper.buildForm(HfbConst.RECHARGE_URL, params);

        /*
            将上面充值要提交给hbf的数据保存到本地数据库表中：
                以后对账使用
         */
        //将要提交的充值请求的数据在本地备份
        //一个账户可以多次充值: 一个user_id每次充值时 都会生成一条记录，每个充值记录都有chargeNo代表唯一的一个充值信息
        UserAccount userAccount = new UserAccount();
        userAccount.setAmount(amount);
        userAccount.setFreezeAmount(new BigDecimal("0"));
        userAccount.setUserId(userInfo.getId());
        userAccount.setStatus(0);
        userAccount.setChargeNo(chargeNo);
        this.save(userAccount);
        return form;
    }

    @Override
    public String chargeAmountNotify(HttpServletRequest request) {
        //获取到异步通知的结果
        Map<String, String[]> parameterMap = request.getParameterMap();
        Map<String, Object> map = RequestHelper.switchMap(parameterMap);
        //验证通知的结果签名正确
        boolean b = RequestHelper.isSignEquals(map);
        if(!b){
            //签名验证失败： 数据被篡改
            log.error("充值失败，原因: 签名验证失败, 充值返回结果：{}" , JSON.toJSONString(map));
            return "fail";
        }
        //用户充值成功回调传过来的汇付宝用户充值的数据
        BigDecimal chargeAmt = new BigDecimal(map.get("chargeAmt").toString());
        BigDecimal hyFee = new BigDecimal(map.get("hyFee").toString());//汇付宝手续费
        BigDecimal mchFee = new BigDecimal(map.get("mchFee").toString());//商户手续费
        //充值成功的用户的唯一标志(bindCode代表充值用户绑定hfb账户时生成的唯一编号)
        String bindCode = map.get("bindCode").toString();
        //用户充值我们生成的交易流水号：可以在user_account表中查找到之前的充值记录
        String agentBillNo = map.get("agentBillNo").toString();
        String resultMsg = map.get("resultMsg").toString();

        //判断回调方法是否为重复调用
        int count = transFlowService.count(new LambdaQueryWrapper<TransFlow>()
                .eq(TransFlow::getTransNo, agentBillNo));
        if(count>0){
            //已经处理过本次充值完成的回调  无需再次处理
            return "success";
        }

        //获取用户在汇付宝充值前本地保存的用户充值的数据
        UserAccount userAccount = this.getOne(new LambdaQueryWrapper<UserAccount>().eq(UserAccount::getChargeNo,
                agentBillNo));
        //充值成功返回的结果：{"chargeAmt":"500","hyFee":"0","mchFee":"0","resultCode":"0001",
        // "bindCode":"fb573513f1454bb3842734b87f1a58c3","agentBillNo":"CHARGE20221109102619617",
        // "resultMsg":"充值成功","timestamp":"1667960818270"}
        String resultCode = map.get("resultCode").toString();
        if(!StringUtils.equals("0001",resultCode)){
            //更新userAccount充值状态为失败
            if(userAccount!=null){
                userAccount.setStatus(-1);
                this.updateById(userAccount);
            }
            log.error("充值失败，原因: 汇付宝返回失败, 充值返回结果：{}" , JSON.toJSONString(map));
            return "fail";
        }


        if(userAccount==null || userAccount.getAmount().compareTo(chargeAmt)!=0){//验证用户在汇付宝充值的金额是否和提交充值请求时一样
            log.error("充值失败，原因: 汇付宝充值金额和提交的充值金额不一致：{}" , JSON.toJSONString(map));
            if(userAccount!=null){
                userAccount.setStatus(-1);
                this.updateById(userAccount);
            }
            return "fail";
        }
        log.info("充值成功返回的结果：{}" ,JSON.toJSONString(map));
        //如果验证全部通过：更新用户充值的状态为成功
        userAccount.setStatus(1);
        this.updateById(userAccount);
        //bindCode: 查询用户数据
        UserInfo userInfo = userInfoService.getOne(new LambdaQueryWrapper<UserInfo>()
                .eq(UserInfo::getBindCode, bindCode));
        TransFlow transFlow = new TransFlow();
        transFlow.setUserId(userInfo.getId());
        transFlow.setUserName(userInfo.getName());
        //交易类型（1：充值 2：提现 3：投标 4：投资回款 ...）
        transFlow.setTransType(1);
        transFlow.setTransTypeName("充值");
        transFlow.setTransNo(agentBillNo);
        transFlow.setTransAmount(chargeAmt);
        transFlow.setMemo(JSON.toJSONString(map));
        transFlowService.save(transFlow);
        //充值成功：发送短信
        //整合rabbitmq：
        //1、生产者：发送mq消息
        Map<String,String> result = new HashMap<>();
        result.put("mobile",userInfo.getMobile());
        result.put("content" , "充值成功");
        result.put("tplId" , "1001");
        //充值成功sms根据mq的消息发送短信
        rabbitTemplate.convertAndSend("srb.account.exchange","account.charge" ,
                JSON.toJSONString(result));
        return "success";
    }

    @Override
    public BigDecimal getAmount(String token) {
        Long userId = JwtUtils.getUserId(token);
        //使用userId+status为1  查询该用户的所有的充值记录
        List<UserAccount> userAccounts = this.list(new LambdaQueryWrapper<UserAccount>()
                .eq(UserAccount::getUserId, userId)
                .eq(UserAccount::getStatus, 1));
        BigDecimal amount = new BigDecimal("0");
        if(!CollectionUtils.isEmpty(userAccounts)){
            amount = userAccounts.stream()
                    .map(userAccount -> userAccount.getAmount())
                    .reduce((a, b) -> a.add(b)).get();
        }
        return amount;
    }

    @Override
    public BigDecimal getInterestCount(Integer returnMethod, BigDecimal lendYearRate,
                                       Integer period, BigDecimal investAmount) {
        BigDecimal interestCount = new BigDecimal("0");
        switch (returnMethod){
            case 1: //等额本息
                interestCount = Amount1Helper.getInterestCount(investAmount,lendYearRate,period);
                break;
            case 2://等额本金
                interestCount = Amount2Helper.getInterestCount(investAmount,lendYearRate,period);
                break;
            case 3://每月还息一次还本
                interestCount = Amount3Helper.getInterestCount(investAmount,lendYearRate,period);
                break;
            case 4://一次还本还息
                interestCount = Amount4Helper.getInterestCount(investAmount,lendYearRate,period);
                break;

        }
        return interestCount;
    }

    @Override
    public String commitWithdraw(String token, BigDecimal amount) {
//        Long userId = JwtUtils.getUserId(token);
        UserInfo userInfo = userInfoService.checkAccount(token);
        //提现金额不能超过账户金额
        BigDecimal accountAmount = this.getAmount(token);
        if(amount.compareTo(accountAmount)==1  || amount.compareTo(new BigDecimal("0"))!=1){
            //提现金额超过账户金额  提现金额不能小于等于0
            throw new BusinessException(ResultCodeEnum.ACCOUNT_AMOUNT_LESS_ERROR);
        }
        Map<String, Object> params = new HashMap<>();
        params.put("agentId",HfbConst.AGENT_ID);
        params.put("agentBillNo",LendNoUtils.getWithdrawNo());
        //获取提现用户的bindCode
        params.put("bindCode",userInfo.getBindCode());
        params.put("fetchAmt",amount);
        params.put("feeAmt",new BigDecimal("0"));
        params.put("returnUrl",HfbConst.WITHDRAW_RETURN_URL);
        //需要修改为nginx的地址
        params.put("notifyUrl",HfbConst.WITHDRAW_NOTIFY_URL);
        params.put("timestamp",System.currentTimeMillis());
        params.put("sign",RequestHelper.getSign(params));
        return FormHelper.buildForm(HfbConst.WITHDRAW_URL,params);
    }

    @Transactional(rollbackFor = {Exception.class})
    @Override
    public String notifyWithdraw(HttpServletRequest request) {
        Map<String, String[]> parameterMap = request.getParameterMap();
        Map<String, Object> map = RequestHelper.switchMap(parameterMap);
        System.out.println("提现回调结果："+JSON.toJSONString(map));
        //验签
        if(!RequestHelper.isSignEquals(map)){
            return "fail";
        }
        //校验响应的状态码是否成功
        if(!"0001".equals(map.get("resultCode"))){
            //hfb处理提现业务失败
            return "fail";
        }
        //更新本地账户余额
        UserInfo userInfo = userInfoService.getOne(new LambdaQueryWrapper<UserInfo>()
                .eq(UserInfo::getBindCode, map.get("bindCode")));
        //获取userAccount对象：
        UserAccount userAccount = this.getOne(new LambdaQueryWrapper<UserAccount>()
                .eq(UserAccount::getUserId, userInfo.getId())
                .orderByDesc(UserAccount::getAmount)
                .last("limit 1"));
        userAccount.setAmount(userAccount.getAmount().subtract(new BigDecimal(map.get("fetchAmt").toString())));
        this.updateById(userAccount);//更新账户余额
        // 保存提现流水
        TransFlow transFlow = new TransFlow();
        transFlow.setMemo(JSON.toJSONString(map));
        transFlow.setTransAmount(new BigDecimal(map.get("fetchAmt").toString()));
        transFlow.setTransNo(map.get("agentBillNo").toString());//获取提现生成的交易编号
        transFlow.setTransType(2);
        transFlow.setTransTypeName("提现");
        transFlow.setUserId(userInfo.getId());
        transFlow.setUserName(userInfo.getName());
        transFlowService.save(transFlow);
        return "success";
    }


}
